﻿namespace Knownet.AST.Source
{
  using System;
  using System.IO;
  using System.Text;

  /// <summary>
  /// 分隔符（包括操作符）对齐方式。
  /// </summary>
  [Flags]
  public enum SeparatorAlignment
  {
    Default = 0x00000000,
    /// <summary>
    /// 操作符放置于中央，两边没有
    /// 单词分隔符包围。
    /// </summary>
    Compact = 0x00000001,
    /// <summary>
    /// 操作符放置于左侧，只有右边由
    /// 单词分隔符包围。
    /// </summary>
    Left = 0x00000002,
    /// <summary>
    /// 操作符放置于右侧，只有左边由
    /// 单词分隔符包围。
    /// </summary>
    Right = 0x00000003,
    /// <summary>
    /// 操作符放置于中央，左右两边由
    /// 单词分割符包围。
    /// </summary>
    Center = 0x00000004,
  }
  /// <summary>
  /// 内容对齐方式。
  /// </summary>
  [Flags]
  public enum ContentAlignment
  {
    /// <summary>
    /// 缺省方式，无缩进和单行。
    /// </summary>
    Default = 0x00000000,
    /// <summary>
    /// 起始行，不和先前内容共享一行。
    /// </summary>
    StartLine = 0x00000001,
    /// <summary>
    /// 终止行，不和后续内容共享一行。
    /// </summary>
    EndLine = 0x00000002,
    /// <summary>
    /// 单行，不和其他内容共享一行，
    /// 包括LineStart和LineEnd。
    /// </summary>
    SingleLine = 0x00000003,
    /// <summary>
    /// 缩进
    /// </summary>
    Ident = 0x00000004,
  }

  /// <summary>
  /// AST打印机
  /// </summary>
  /// <remarks>
  /// 在已知AST定义的前提下，
  /// 将AST树的内容递归打印
  /// 出来。
  /// </remarks>
  public class AstSourcePrinter
  {
    #region Statics
    /// <summary>
    /// 缺省的Tab空格数。
    /// </summary>
    public static int DefaultTabs = 4;
    public static char DefaultTabChar = '\t';
    public static char SpaceTabChar = ' ';
    #endregion
    #region Fields
    protected int indentLevel = 0;
    protected bool lineBeginning = false;
    protected TextWriter output = null;
    protected string tabs = string.Empty;
    protected string wordSeparator = SpaceTabChar.ToString();
    protected string paraSeparator = TextWriter.Null.NewLine; //"\r\n" for windows platform.
    protected SeparatorAlignment separatorAlignment = SeparatorAlignment.Default;
    #endregion
    #region Properties
    public virtual SeparatorAlignment SeparatorAlignment
    {
      get
      {
        return this.separatorAlignment;
      }
      set
      {
        this.separatorAlignment = value;
      }
    }
    /// <summary>
    /// 获取当前打印机的输出。
    /// </summary>
    public virtual TextWriter Output
    {
      get
      {
        return this.output;
      }
    }
    /// <summary>
    /// 获取或者设定缺省的Tab空格表示。
    /// -1代表"\t"。
    /// </summary>
    public virtual int TabSpaces
    {
      get
      {
        return (this.tabs == DefaultTabChar.ToString()) ? -1 : this.tabs.Length;
      }
      set
      {

        if (value == -1)
        {
          this.tabs = DefaultTabChar.ToString();
        }
        else
        {
          value = (value >= 0) ? value : DefaultTabs;
          this.tabs = new string(SpaceTabChar, value);
        }
      }
    }
    /// <summary>
    /// 获取或者设置当前的文字分隔符。
    /// </summary>
    public virtual string WordSeparator
    {
      get
      {
        return this.wordSeparator;
      }
      set
      {
        this.wordSeparator = (value != null) ? value : string.Empty;
      }
    }
    /// <summary>
    /// 获取或者设置当前的段落分隔符。
    /// </summary>
    public virtual string ParaSeparator
    {
      get
      {
        return this.paraSeparator;
      }
      set
      {
        this.paraSeparator = (value != null) ? value : string.Empty;
      }
    }
    /// <summary>
    /// 获取当前缩进的空白（空格）数量。
    /// </summary>
    protected virtual int IdentSpaces
    {
      get
      {
        return this.TabSpaces * this.indentLevel;
      }
    }
    #endregion
    #region Constructors
    public AstSourcePrinter()
    {

    }
    #endregion
    #region Methods
    #region Printing and styling

    protected virtual void PrintSeparator(string separator)
    {
      this.PrintSeparator(separator, true);
    }
    protected virtual void PrintSeparator(string separator, bool print)
    {
      this.PrintSeparator(separator, SeparatorAlignment.Compact, print);
    }

    protected virtual void PrintSeparator(string separator, SeparatorAlignment alignment, bool print)
    {
      if (print)
      {
        switch (alignment)
        {
          case SeparatorAlignment.Compact:
            this.Print(separator);
            break;
          case SeparatorAlignment.Center:
            this.Print(this.WordSeparator);
            this.Print(separator);
            this.Print(this.WordSeparator);
            break;
          case SeparatorAlignment.Left:
            this.Print(separator);
            this.Print(this.WordSeparator);
            break;
          case SeparatorAlignment.Right:
            this.Print(this.WordSeparator);
            this.Print(separator);
            break;
          default:
            this.Print(separator);
            break;
        }
      }
    }
    protected virtual void PrintWord(string s)
    {
      bool lineFollowing = !this.IsAtLineBeginning();
      this.Print(s);
      if (lineFollowing)
      {
        this.Print(this.WordSeparator);
      }
    }
    protected virtual void Print(string s)
    {
      if (this.IsAtLineBeginning())
      {
        this.PrintIndent();
        this.ClearLineBeginning();
      }
      this.PrintDirectly(s);
    }
    protected virtual void IndentOpen_(string b)
    {
      this.PrintLine(b);
      this.Indent();
    }
    protected virtual void IndentClose(string b)
    {
      this.Unindent();
      this.PrintLine(b);
    }
    protected virtual void PrintLine()
    {
      this.PrintLineDirectly();
      this.SetLineBeginning();
    }
    protected virtual void PrintLine(string s)
    {
      this.Print(s);
      this.PrintLine();
      this.SetLineBeginning();
    }
    protected virtual void PrintIndent()
    {
      for (int i = 0; i < this.indentLevel; i++)
      {
        this.PrintDirectly(this.tabs);
      }
    }
    protected virtual void PrintLineDirectly()
    {
      this.Output.WriteLine();
    }
    protected virtual void PrintDirectly(string s)
    {
      this.Output.Write(s);
    }
    protected virtual void Indent()
    {
      this.indentLevel++;
    }
    protected virtual void Unindent()
    {
      this.indentLevel--;
    }

    protected virtual bool SetLineBeginning()
    {
      return this.lineBeginning = true;
    }
    protected virtual bool ClearLineBeginning()
    {
      return this.lineBeginning = false;
    }
    protected virtual bool IsAtLineBeginning()
    {
      return this.lineBeginning;
    }
    #endregion
    #endregion
  }
}
